/**
 * Created by Forcs on 16/4/10.
 */

var mongoose = require('mongoose');
var model = require('../db/Model');
var Flavors = require('../models/Flavors');
var Products = require('../models/Products');
var Permissions = require('../models/Permissions');
var co = require('co');
var utils = require('../utils/Utils');

var Manager = function () {
  var self = this;
  
  self._getTotalDoc = function (biz) {
    return new Promise(function (resolve) {
      co(function * () {
        var doc = yield self.$model.findOne({ biz: biz }).exec();
        if (!doc) {
          doc = new self.$model(
            self._initDataItem(
              biz,
              yield Flavors.$manager.allWithBiz(biz,
                {
                  converter: function (item) {
                    return {
                      id: item.id,
                      name: item.name,
                      biz: biz,
                      data: self._initDefaultData()
                    }
                  }
                }
              ),
              yield Products.$manager.allWithBiz(biz)
            )
          );
          yield doc.save();
        }
        resolve(doc);
      }).catch(function (err) {
        resolve(err);
      })
    });
  };

  self._initDefaultData = function () {
    return {
      durations: 0,
      playCount: 0,
      visited: 0,
      users: 0,
      dau: 0,
      wau: 0,
      mau: 0
    }
  };

  self._initDataItem = function (biz, flavors, products) {
    var defData = self._initDefaultData();

    var __create = function (item, defData, flavors) {
      return {
        id: item.code,
        name: item.name,
        data: defData,
        flavors: flavors
      }
    };

    var productDocs = [];
    var categoryDocs = [];
    var contentDocs = [];
    var flavorDocs = [];
    flavors.forEach(function (flavor) {
      if (flavor.biz === biz) {
        flavorDocs.push(flavor);
      }
    });
    products.forEach(function (product) {
      if (product.biz !== biz) {
        return;
      }

      productDocs.push(__create(product, defData, flavors));
      var productId = product.code;
      if (product.categories && product.categories.length) {
        product.categories.forEach(function (category) {
          var cateItem = __create(category, defData, flavors);
          cateItem.product = productId;
          categoryDocs.push(cateItem);
          var categoryId = category.code;
          if (category.contents && category.contents.length) {
            category.contents.forEach(function (content) {
              var contItem = __create(content, defData, flavors);
              contItem.product = productId;
              contItem.category = categoryId;
              contentDocs.push(contItem);
            });
          }
        });
      }
    });

    return {
      data: defData,
      biz: biz,
      flavors: flavorDocs,
      products: productDocs,
      categories: categoryDocs,
      contents: contentDocs
    };
  }
};

Manager.prototype.onProductsModelEvent = function (event, result, doc, flavors) {
  var self = this;
  var item = {
    data: self._initDefaultData(),
    flavors: flavors
  };
  if (result.addedCount && result.added) {
    var add = result.added[0];
    item.id = add.code;
    item.name = add.name;
  } else {
    item.id = result.code;
    item.name = result.name;
  }

  var key = undefined;
  switch (event) {
    case 'add.product':
      key = 'products';
      break;
    case 'add.category':
      item.product = result.product;
      key = 'categories';
      break;
    case 'add.content':
      item.product = result.product;
      item.category = result.category;
      key = 'contents';
      break;
  }
  if (key) {
    var items = doc[key];
    var i = -1;
    if (items && items.length) {
      i = items.length;
      while (i--) {
        if (items[i].id === item.id) {
          break;
        }
      }
    }
    if (i < 0) {
      doc[key].push(item);
    }
  }
};

Manager.prototype.onFlavorsModelEvent = function (event, result, doc) {
  var self = this;
  var item = {
    id: result.id,
    name: result.name,
    data: self._initDefaultData()
  };

  doc.flavors.push(item);
  doc.products.forEach(function (prod) {
    prod.flavors.push(item);
  });
  doc.categories.forEach(function (cate) {
    cate.flavors.push(item);
  });
  doc.contents.forEach(function (cont) {
    cont.flavors.push(item);
  });
};

Manager.prototype.onVisitedEverydayModelEvent = function (event, result) {
  var self = this;
  if (event === 'inc.product.visited') {
    return self.incrementVisitedByProduct(result.biz, result.product, result.isNewUser, result.flavor);
  } else if (event === 'inc.content.visited') {
    return self.incrementVisitedByContent(
      result.biz, result.product, result.category, result.content, result.isNewUser, result.flavor);
  } else if (event === 'inc.content.duration') {
    return self.incrementDurationByContent(
      result.biz, result.product, result.category, result.content, result.duration, result.flavor)
  }
};

Manager.prototype.onEvent = function (model, event, result) {
  var self = this;
  return new Promise(function (resolve, reject) {
    co(function * () {
      var eventResult = undefined;
      var doc = undefined;
      if (model === 'Products') {
        doc = yield self._getTotalDoc(result.biz);
        eventResult = self.onProductsModelEvent(event, result, doc,
          yield Flavors.$manager.allWithBiz(result.biz, {
            converter: function (item) {
              return {
                id: item.id,
                name: item.name,
                biz: result.biz,
                data: self._initDefaultData()
              }
            }
          })
        );
      } else if (model === 'Flavors') {
        doc = yield self._getTotalDoc(result.biz);
        eventResult = self.onFlavorsModelEvent(event, result, doc);
      } else if (model === 'VisitedEveryday') {
        eventResult = self.onVisitedEverydayModelEvent(event, result);
      }
      
      var res = undefined;
      if (utils.isPromise(eventResult)) {
        res = yield eventResult;
      }
      if (model !== 'VisitedEveryday') {
        if (doc) {
          res = yield doc.save();
        }
      }
      resolve(res);
    }).catch(function (err) {
      reject(err);
    });
  });
};

Manager.prototype.incrementVisitedByProduct = function (biz, product, isNewUser, flavor, func) {
  var self = this;
  return new Promise(function (resolve, reject) {
    co(function * () {
      var doc = yield self._getTotalDoc(biz);
      doc.data.incrementVisited(isNewUser);

      var fi = doc.flavors.length || 0;
      while (fi--) {
        var f = doc.flavors[fi];
        if (f.id === flavor) {
          f.incrementDataVisited(isNewUser);
          break;
        }
      }

      var pi = doc.products.length || 0;
      while (pi--) {
        var prod = doc.products[pi];
        if (prod.id === product) {
          prod.data.incrementVisited(isNewUser);
          var pfi = prod.flavors.length || 0;
          while (pfi--) {
            var pf = prod.flavors[pfi];
            if (pf.id === flavor) {
              pf.incrementDataVisited(isNewUser);
              break;
            }
          }
          break;
        }
      }

      var result = yield doc.save();

      if (utils.isFunction(func)) {
        func.call(self, undefined, result);
      }
      resolve(result);
    }).catch(function (err) {
      if (utils.isFunction(func)) {
        func.call(self, err);
      }
      reject(err);
    });
  });
};

Manager.prototype.incrementVisitedByContent = function (biz, product, category, content, isNewUser, flavor, func) {
  var self = this;
  return new Promise(function (resolve, reject) {
    co(function * () {
      var doc = yield self._getTotalDoc(biz);
      doc.data.incrementPlayCount();

      var fi = doc.flavors.length || 0;
      while (fi--) {
        var f = doc.flavors[fi];
        if (f.id === flavor) {
          f.incrementDataPlayCount();
          break;
        }
      }

      var pi = doc.products.length || 0;
      while (pi--) {
        var prod = doc.products[pi];
        if (prod.id === product) {
          prod.data.incrementPlayCount();

          var pfi = prod.flavors.length || 0;
          while (pfi--) {
            var pf = prod.flavors[pfi];
            if (pf.id === flavor) {
              pf.incrementDataPlayCount();
              break;
            }
          }
          break;
        }
      }

      var cai = doc.categories.length || 0;
      while (cai--) {
        var cate = doc.categories[cai];
        if (cate.product === product && cate.id === category) {
          cate.data.incrementVisited(isNewUser)
            .incrementPlayCount();

          var cafi = cate.flavors.length || 0;
          while (cafi--) {
            var caf = cate.flavors[cafi];
            if (caf.id === flavor) {
              caf.incrementDataVisited(isNewUser)
                .incrementDataPlayCount();
              break;
            }
          }
          break;
        }
      }

      var coi = doc.contents.length || 0;
      while (coi--) {
        var cont = doc.contents[coi];
        if (cont.product === product && cont.category === category && cont.id === content) {
          cont.data.incrementVisited(isNewUser)
            .incrementPlayCount();

          var cofi = cont.flavors.length || 0;
          while (cofi--) {
            var cof = cont.flavors[cofi];
            if (cof.id === flavor) {
              cof.incrementDataVisited(isNewUser)
                .incrementDataPlayCount();
              break;
            }
          }
          break;
        }
      }

      var result = yield doc.save();

      if (utils.isFunction(func)) {
        func.call(self, undefined, result);
      }
      resolve(result);
    }).catch(function (err) {
      if (utils.isFunction(func)) {
        func.call(self, err);
      }
      reject(err);
    })
  });
};

Manager.prototype.incrementDurationByContent = function (biz, product, category, content, duration, flavor, func) {
  var self = this;
  return new Promise(function (resolve, reject) {
    co(function * () {
      var doc = yield self._getTotalDoc(biz);
      doc.data.incrementDuration(duration);

      var fi = doc.flavors.length || 0;
      while (fi--) {
        var f = doc.flavors[fi];
        if (f.id === flavor) {
          f.incrementDataDuration(duration);
          break;
        }
      }

      var pi = doc.products.length || 0;
      while (pi--) {
        var prod = doc.products[pi];
        if (prod.id === product) {
          prod.data.incrementDuration(duration);

          var pfi = prod.flavors.length || 0;
          while (pfi--) {
            var pf = prod.flavors[pfi];
            if (pf.id === flavor) {
              pf.incrementDataDuration(duration);
              break;
            }
          }
          break;
        }
      }

      var cai = doc.categories.length || 0;
      while (cai--) {
        var cate = doc.categories[cai];
        if (cate.product === product && cate.id === category) {
          cate.data.incrementDuration(duration);

          var cafi = cate.flavors.length || 0;
          while (cafi--) {
            var caf = cate.flavors[cafi];
            if (caf.id === flavor) {
              caf.incrementDataDuration(duration);
              break;
            }
          }
          break;
        }
      }

      var coi = doc.contents.length || 0;
      while (coi--) {
        var cont = doc.contents[coi];
        if (cont.product === product && cont.category === category && cont.id === content) {
          cont.data.incrementDuration(duration);

          var cofi = cont.flavors.length || 0;
          while (cofi--) {
            var cof = cont.flavors[cofi];
            if (cof.id === flavor) {
              cof.incrementDataDuration(duration);
              break;
            }
          }
          break;
        }
      }

      var result = yield doc.save();

      if (utils.isFunction(func)) {
        func.call(self, undefined, result);
      }
      resolve(result);
    }).catch(function (err) {
      if (utils.isFunction(func)) {
        func.call(self, err);
      }
      reject(err);
    })
  });
};

var dataSchema = new mongoose.Schema({
  durations: Number,
  playCount: Number,
  visited: Number,
  users: Number,
  dau: Number,
  wau: Number,
  mau: Number
});

dataSchema.methods.incrementVisited = function () {
  var inc = 1;
  if (arguments.length) {
    if (arguments.length > 1) {
      inc = parseInt(arguments[1]);
    }
    if (!!arguments[0]) {
      this.users += inc;
    }
  }
  this.visited += inc;
  return this;
};

dataSchema.methods.incrementDuration = function (inc) {
  this.durations += inc;
  return this;
};

dataSchema.methods.incrementPlayCount = function () {
  var inc = 1;
  if (arguments.length) {
    inc = parseInt(arguments[0]);
  }
  this.playCount += inc;
  return this;
};

var flavorSchema = new mongoose.Schema({
  id: String,
  name: String,
  data: dataSchema
});

flavorSchema.methods.incrementDataVisited = function () {
  this.data.incrementVisited.apply(this.data, utils.toArray(arguments));
  return this;
};

flavorSchema.methods.incrementDataDuration = function () {
  this.data.incrementDuration.apply(this.data, utils.toArray(arguments));
  return this;
};

flavorSchema.methods.incrementDataPlayCount = function () {
  this.data.incrementPlayCount.apply(this.data, utils.toArray(arguments));
  return this;
};

var hourSchema = new mongoose.Schema({
  hour: Number,
  visited: Number,
  durations: Number,
  playCount: Number
});

module.exports = model.create('VisitedTotal', {
  biz: String,
  data: dataSchema,
  flavors: [ flavorSchema ],
  hours: [hourSchema],
  products: [
    {
      id: String,
      name: String,
      data: dataSchema,
      hours: [ hourSchema ],
      flavors: [ flavorSchema ]
    }
  ],
  categories: [
    {
      id: String,
      name: String,
      product: String,
      data: dataSchema,
      hours: [ hourSchema ],
      flavors: [ flavorSchema ]
    }
  ],
  contents: [
    {
      id: String,
      name: String,
      product: String,
      category: String,
      doneCount: Number,  // 内容别完整看完的次数
      data: dataSchema,
      hours: [ hourSchema ],
      flavors: [ flavorSchema ]
    }
  ]
}, Manager);